
/*********************************************************************
* Include file
**********************************************************************/
#include <stdio.h>
#include <string.h>
#include "project.h"
#include "drv_l1_sfr.h"
#include "drv_l1_usbd.h"
#include "drv_l2_usbd.h"
#include "drv_l2_usbd_msdc.h"
#include "drv_l2_usbd_uvc_uc.h"
#include "application.h"
#include "ap_state_handling.h"
#include "ap_peripheral_handling.h"
#include "state_avi_encoder.h"
#include "drv_l2_usbh_uvc.h"

/**************************************************************************
* CONSTANTS
**************************************************************************/
typedef enum
{
	/* 0 for system DMA done */
	USBD_START_VIDEO_EVENT		= 1,
	USBD_STOP_VIDEO_EVENT,
	USBD_SEND_VIDEO_EVENT,
	USBD_SEND_VIDEO_DONE_EVENT,
	USBD_START_AUDIO_EVENT,
	USBD_STOP_AUDIO_EVENT,
	USBD_SEND_AUDIO_DONE_EVENT,
	USBD_SET_INTERFACE_EVENT,
	USBD_UAC_TIMER_DONE,
	USBD_NO_EVENT
} UVC_TASK_EVENT_E;

#define C_USB_CAM_STATE_STACK_SIZE	512
INT32U	USBCamStateStack[C_USB_CAM_STATE_STACK_SIZE];

#define USB_CAM_QUEUE_MAX_LEN	20
void	*USBCamApQ_Stack[USB_CAM_QUEUE_MAX_LEN];

#define USB_MODE_CHARGING	0
#define USB_MODE_CONNECT_PC 1

#define USB_HOST_DETECTED_CNT	100	/* 1000ms */

#if defined(BACK_SENSOR_NON_PIN_DET)&& BACK_SENSOR_NON_PIN_DET
extern INT8U in_USBD_mode_flag;
#endif

enum
{
	USBD_ENGINE_STATUS_IDLE			= 0,
	USBD_ENGINE_STATUS_SEND_VIDEO,
	USBD_ENGINE_STATUS_SEND_AUDIO
};

/**************************************************************************
* MACROS
**************************************************************************/

/**************************************************************************
 * DATA TYPES
 **************************************************************************/

/**************************************************************************
* GLOBAL DATA
**************************************************************************/
static INT8U msdc_mode_flag = USB_MODE_CHARGING;

static INT32U send_video = 0;
static INT32U send_audio = 0;

static INT32U uvc_send_buf;
static INT32U uvc_send_buf_size;

OS_EVENT *UVCTaskQ = NULL;

/**************************************************************************
* EXTERN DATA
**************************************************************************/
// MSDC
extern INT8U Default_Device_Descriptor_TBL[];
extern INT8U Default_Config_Descriptor_TBL[];
extern INT8U Default_Qualifier_Descriptor_TBL[];
extern INT8U Default_String0_Descriptor[];
extern INT8U Default_String1_Descriptor[];
extern INT8U Default_String2_Descriptor[];
extern INT8U Default_scsi_inquirydata[];
extern INT8U Default_scsi_inquirydata_CDROM[];

// SD
extern const MSDC_LUN_STORAGE_DRV gp_msdc_ramdisk;
extern const MSDC_LUN_STORAGE_DRV gp_msdc_sd1;

//SPI FLASH DISK
#if C_SPI_FLASH_DISK
	extern const MSDC_LUN_STORAGE_DRV SPI_FLASH_DISK_Access;
#endif

//UVC
extern INT8U USB_UVC_MJPEG_DeviceDescriptor[];
extern INT8U USB_UVC_MJPEG_Qualifier_Descriptor_TBL[];
extern INT8U USB_UVC_MJPEG_ConfigDescriptor[];
extern INT8U UVC_MJPEG_String0_Descriptor[];
extern INT8U UVC_MJPEG_String1_Descriptor[];
extern INT8U UVC_MJPEG_String2_Descriptor[];
extern INT8U UVC_MJPEG_String3_Descriptor[];
extern INT8U UVC_MJPEG_String4_Descriptor[];
extern INT8U UVC_MJPEG_String5_Descriptor[];

/*************************************************************************/

/*
 *	usb_mode_detect_callback
 */
void usb_mode_detect_callback(void)
{
	if(!back_a_car_status_get())
	{
		drv_l1_usbd_soft_disconnect();
		msdc_mode_flag = USB_MODE_CONNECT_PC;
	}
}

INT32U usb_device_is_connected_to_pc(void)
{
	return ((msdc_mode_flag == USB_MODE_CONNECT_PC)?1:0);
}

/*************************************************************************/

/*
 *	usb_mode_detect
 */
INT32S usb_mode_detect(void)
{
	INT32S	ret = STATUS_OK;
	INT32U	time_current = 0;

	msdc_mode_flag = USB_MODE_CHARGING;

#ifdef USB_PATH_CONTROL
	gpio_write_io(USB_PATH_CONTROL, DATA_LOW); //switch USB path to pc
#endif

	/* Enable USB PHY and switch to device mode */
	drv_l1_usbd_phy_clk_on();

	OSTimeDly(1);

	/* Init control state handler */
	drv_l2_usbd_register_state_handler(USBD_XFER_CONTROL, drv_l2_usbd_state_ctl_ep0_setup, NULL, NULL);

	drv_l2_usbd_register_state_handler(USBD_XFER_MISC, drv_l2_usbd_misc_handle, NULL, NULL);

	drv_l2_usbd_register_detect_cbk((void *) usb_mode_detect_callback);

	drv_l1_usbd_set_l1tol2_msg_type(1);

	/* Init USBD L1 register layer */
	ret = drv_l1_usbd_init();
	if(ret == STATUS_FAIL)
	{
		DBG_PRINT("drv_l1_usbd_init failed!\r\n");
		return ret;
	}

	/* Set PHY config */
	drv_l1_usbd_phy_config(USBD_PHY_SETTING);
	
	/* register USBD ISR handler */
	drv_l1_usbd_enable_isr(drv_l1_usbd_isr_handle);

	/* Check  Seup command from pc */
	while(msdc_mode_flag == USB_MODE_CHARGING)
	{
		if(time_current++ > USB_HOST_DETECTED_CNT)
			break;
		OSTimeDly(1);
	}

	drv_l1_usbd_disable_isr();

	drv_l2_usbd_register_state_handler(USBD_XFER_CONTROL, NULL, NULL, NULL);

	drv_l2_usbd_register_detect_cbk(NULL);

	drv_l1_usbd_set_l1tol2_msg_type(0);

	if(msdc_mode_flag == USB_MODE_CONNECT_PC)
	{
		msgQSend(ApQ, MSG_APQ_CONNECT_TO_PC, NULL, 0, MSG_PRI_NORMAL);
	}
	#if defined(BACK_SENSOR_NON_PIN_DET)&& BACK_SENSOR_NON_PIN_DET
	else
	{
		in_USBD_mode_flag = 0;
#ifdef USB_PATH_CONTROL
		gpio_write_io(USB_PATH_CONTROL, DATA_HIGH); //switch USB path to rear sensor
#endif
	}
	#endif

	return ret;
}

/*************************************************************************/

/*
 *	usb_msdc_init
 */
INT32S usb_msdc_init(void)
{
	INT32S	ret = STATUS_OK;

	/* Enable USB PHY and switch to device mode */
	drv_l1_usbd_phy_clk_on();

	/* Init USBD L2 protocol layer first, including control/bulk/ISO/interrupt transfers */
	/******************************* Control transfer ************************************/
	ret = drv_l2_usbd_ctl_init(PRIORITY_USBD_L2);
	if(ret == STATUS_FAIL)
	{
		DBG_PRINT("drv_l2_usbd_ctl_init failed!\r\n");
		return ret;
	}

	/* Register new descriptor table here, this action must be done after drv_l2_usbd_ctl_init() */
	drv_l2_usbd_register_descriptor(REG_DEVICE_DESCRIPTOR_TYPE, (INT8U *) Default_Device_Descriptor_TBL);
	drv_l2_usbd_register_descriptor(REG_CONFIG_DESCRIPTOR_TYPE, (INT8U *) Default_Config_Descriptor_TBL);
	drv_l2_usbd_register_descriptor(REG_DEVICE_QUALIFIER_DESCRIPTOR_TYPE, (INT8U *) Default_Qualifier_Descriptor_TBL);
	drv_l2_usbd_register_descriptor(REG_STRING0_DESCRIPTOR_TYPE, (INT8U *) Default_String0_Descriptor);
	drv_l2_usbd_register_descriptor(REG_STRING1_DESCRIPTOR_TYPE, (INT8U *) Default_String1_Descriptor);
	drv_l2_usbd_register_descriptor(REG_STRING2_DESCRIPTOR_TYPE, (INT8U *) Default_String2_Descriptor);

	if(ap_state_handling_storage_id_get() == NO_STORAGE)
	{
		#if C_SPI_FLASH_DISK
			drv_l2_usbd_msdc_set_lun(LUN_RAM_DISK_TYPE, LUN_NUM_0, USBD_STORAGE_NO_WPROTECT, &SPI_FLASH_DISK_Access);
		#else
			drv_l2_usbd_msdc_set_lun(LUN_RAM_DISK_TYPE, LUN_NUM_0, USBD_STORAGE_NO_WPROTECT, &gp_msdc_ramdisk);
		#endif
	}
	else
	{
		drv_l2_usbd_msdc_set_lun(LUN_SDC_TYPE, LUN_NUM_0, USBD_STORAGE_NO_WPROTECT, &gp_msdc_sd1);
	}

	/* Init MSDC driver */
	ret = drv_l2_usbd_msdc_init();
	if(ret == STATUS_FAIL)
	{
		/* Init failed, do uninit procedures */
		drv_l2_usbd_msdc_uninit();
		DBG_PRINT("drv_l2_usbd_msdc_uninit failed!\r\n");
		return ret;
	}

	/* Register SCSI inquiry data pointer, it must be done after drv_l2_usbd_msdc_init() */
	drv_l2_usbd_msdc_register_scsi_inquiry_data((INT8U *) Default_scsi_inquirydata, (INT8U *) Default_scsi_inquirydata_CDROM);

	/* Init USBD L1 register layer */
	ret = drv_l1_usbd_init();
	if(ret == STATUS_FAIL)
	{
		DBG_PRINT("drv_l1_usbd_init failed!\r\n");
		return ret;
	}

	/* Set PHY config */
	drv_l1_usbd_phy_config(USBD_PHY_SETTING);

	/* register USBD ISR handler */
	drv_l1_usbd_enable_isr(drv_l1_usbd_isr_handle);
	DBG_PRINT("USB MSDC device init completed!\r\n");

	return ret;
}

/*************************************************************************/

/*
 *	usb_msdc_exit
 */
void usb_msdc_exit(void)
{
	drv_l1_usbd_uninit();
	drv_l2_usbd_ctl_uninit();
	drv_l2_usbd_msdc_uninit();
}

/*************************************************************************/

/*
 *	_usbd_ep5_send_done_cbk
 */
static void _usbd_ep5_send_done_cbk(void)
{
	/* Set EP5 frame end flag for UVC header */
	drv_l1_usbd_set_frame_end_ep5();



	if(OSQPost(UVCTaskQ, (void *) USBD_SEND_VIDEO_DONE_EVENT) != OS_NO_ERR)
	{
		DBG_PRINT("EP5 done cbk send event to UVCTaskQ failed\r\n");
	}
}

/*************************************************************************/

/*
 *	_usbd_ep7_send_done_cbk
 */
static void _usbd_ep7_send_done_cbk(void)
{
	if(OSQPost(UVCTaskQ, (void *) USBD_SEND_AUDIO_DONE_EVENT) != OS_NO_ERR)
	{
		DBG_PRINT("EP7 done cbk send event to UVCTaskQ failed\r\n");
	}
}

/*************************************************************************/

/*
 *	_usbd_get_set_interface
 */
void _usbd_get_set_interface(INT32U video, INT32U audio)
{
	INT32U	msg = USBD_NO_EVENT;

	/* Check Video */
	if(send_video != video)
	{
		if(video)
			msg = USBD_START_VIDEO_EVENT;
		else
			msg = USBD_STOP_VIDEO_EVENT;

		send_video = video;
	}

	/* Check Audio */
	if(send_audio != audio)
	{
		if(audio)
			msg = USBD_START_AUDIO_EVENT;
		else
			msg = USBD_STOP_AUDIO_EVENT;

		send_audio = audio;
	}

	if(msg != USBD_NO_EVENT)
	{
		if(OSQPost(UVCTaskQ, (void *) msg) != OS_NO_ERR)
		{
			DBG_PRINT("Send event to UVCTaskQ failed\r\n");
		}
	}
}

/*************************************************************************/

/*
 *	usb_webcam_start
 */
INT32S usb_webcam_start(void)
{
	INT32S	nRet;

	nRet = STATUS_OK;
	if((avi_encode_get_status() & C_AVI_ENCODE_USB_WEBCAM) == 0)
	{
		POST_MESSAGE(videoEncodeApQ, MSG_AVI_START_USB_WEBCAM, videoEncodeMsgAck, 5000);
		avi_encode_set_status(C_AVI_ENCODE_USB_WEBCAM);
	}

Return:
	return nRet;
}

/*************************************************************************/

/*
 *	usb_webcam_stop
 */
INT32S usb_webcam_stop(void)
{
	INT32S	nRet;

	nRet = STATUS_OK;
	if(avi_encode_get_status() & C_AVI_ENCODE_USB_WEBCAM)
	{
		POST_MESSAGE(videoEncodeApQ, MSG_AVI_STOP_USB_WEBCAM, videoEncodeMsgAck, 5000);
		avi_encode_clear_status(C_AVI_ENCODE_USB_WEBCAM);
	}

Return:
	avi_enc_buffer_free();
	return nRet;
}

/****************************************************************************/

/*
 *	usb_jpeg_send
 */
INT32S usb_jpeg_send(INT32U buf, INT32U size)
{
	uvc_send_buf = buf;
	uvc_send_buf_size = size;

	OSQPost(UVCTaskQ, (void *) USBD_SEND_VIDEO_EVENT);

	return STATUS_OK;
}

/*************************************************************************/

/*
 *	UVCTask
 */
static void UVCTask(void *param)
{
	INT8U	err;
	INT32U	msg;

	while(1)
	{
		msg = (INT32U) OSQPend(UVCTaskQ, 0, &err);
		if(err != OS_NO_ERR)
		{
			continue;
		}

		switch(msg)
		{
		case USBD_START_VIDEO_EVENT:
			usb_webcam_start();
			break;

		case USBD_SEND_VIDEO_DONE_EVENT:
			OSQPost(videoEncodeApQ, (void *) PCCAM_MSG_VIDEO_WRITE_DONE);
			break;

		case USBD_SEND_VIDEO_EVENT:
			drv_l1_usbd_frame_iso_ep5_in((void *) uvc_send_buf, uvc_send_buf_size, 1);
			break;

		case USBD_STOP_VIDEO_EVENT:
			//DBG_PRINT("YUV end\r\n");
			usb_webcam_stop();
			break;

		case USBD_START_AUDIO_EVENT:
			DBG_PRINT("MIC start\r\n");
			break;

		case USBD_SEND_AUDIO_DONE_EVENT:
			break;

		case USBD_STOP_AUDIO_EVENT:
			DBG_PRINT("MIC end\r\n");
			break;

		default:
			break;
		}
	}
}

/*************************************************************************/

/*
 *	usb_uvc_init
 */
INT32S usb_uvc_init(void)
{
	INT8U	err;
	INT32S	ret = STATUS_OK;

	/* Enable USB PHY and switch to device mode */
	drv_l1_usbd_phy_clk_on();

	/* Init USBD L2 protocol layer first, including control/bulk/ISO/interrupt transfers */
	/******************************* Control transfer ************************************/
	ret = drv_l2_usbd_ctl_init(PRIORITY_USBD_L2);
	if(ret == STATUS_FAIL)
	{
		DBG_PRINT("drv_l2_usbd_ctl_init failed!\r\n");
		return ret;
	}

	/* Register new descriptor table here, this action must be done after drv_l2_usbd_ctl_init() */
	drv_l2_usbd_register_descriptor(REG_DEVICE_DESCRIPTOR_TYPE, (INT8U *) USB_UVC_MJPEG_DeviceDescriptor);
	drv_l2_usbd_register_descriptor(REG_CONFIG_DESCRIPTOR_TYPE, (INT8U *) USB_UVC_MJPEG_ConfigDescriptor);
	drv_l2_usbd_register_descriptor(REG_DEVICE_QUALIFIER_DESCRIPTOR_TYPE, (INT8U *) USB_UVC_MJPEG_Qualifier_Descriptor_TBL);
	drv_l2_usbd_register_descriptor(REG_STRING0_DESCRIPTOR_TYPE, (INT8U *) UVC_MJPEG_String0_Descriptor);
	drv_l2_usbd_register_descriptor(REG_STRING1_DESCRIPTOR_TYPE, (INT8U *) UVC_MJPEG_String1_Descriptor);
	drv_l2_usbd_register_descriptor(REG_STRING2_DESCRIPTOR_TYPE, (INT8U *) UVC_MJPEG_String2_Descriptor);
	drv_l2_usbd_register_descriptor(REG_STRING3_DESCRIPTOR_TYPE, (INT8U *) UVC_MJPEG_String3_Descriptor);
	drv_l2_usbd_register_descriptor(REG_STRING4_DESCRIPTOR_TYPE, (INT8U *) UVC_MJPEG_String4_Descriptor);
	drv_l2_usbd_register_descriptor(REG_STRING5_DESCRIPTOR_TYPE, (INT8U *) UVC_MJPEG_String5_Descriptor);

	/* Init USBD L2 of UVC */
	ret = drv_l2_usbd_uvc_init();
	if(ret == STATUS_FAIL)
	{
		DBG_PRINT("drv_l2_usbd_uvc_init failed!\r\n");
		return ret;
	}

	/* Register EP5 sending done call back function */
	drv_l2_usbd_register_uvc_frame_done_cbk(_usbd_ep5_send_done_cbk);

	/* Register EP7 sending done call back function */
	drv_l2_usbd_register_uac_frame_done_cbk(_usbd_ep7_send_done_cbk);

	/* Register set interface call back */
	drv_l2_usbd_register_set_interface_cbk(_usbd_get_set_interface);

	UVCTaskQ = OSQCreate(USBCamApQ_Stack, USB_CAM_QUEUE_MAX_LEN);

	if(UVCTaskQ == NULL)
	{
		DBG_PRINT("UVCTaskQ create failed\r\n");
	}

	err = OSTaskCreate(UVCTask, NULL, &USBCamStateStack[C_USB_CAM_STATE_STACK_SIZE - 1], PRIORITY_USB_CAM);

	if(err != OS_NO_ERR)
	{
		DBG_PRINT("UVCTaskQ create failed\r\n");
	}

	/* Init USBD L1 register layer */
	ret = drv_l1_usbd_uvc_init();
	if(ret == STATUS_FAIL)
	{
		DBG_PRINT("drv_l1_usbd_uvc_init failed!\r\n");
		return ret;
	}

	/* Set PHY config */
	drv_l1_usbd_phy_config(USBD_PHY_SETTING);

	/* register USBD ISR handler */
	drv_l1_usbd_enable_isr(drv_l1_usbd_isr_handle);
	DBG_PRINT("USB UVC device init completed\r\n");

	return ret;
}

/*************************************************************************/

/*
 *	usb_uvc_exit
 */
void usb_uvc_exit(void)
{
	INT8U	err;

	usb_webcam_stop();

	drv_l1_usbd_uninit();
	drv_l2_usbd_ctl_uninit();
	drv_l2_usbd_uvc_uninit();

	send_video = 0;
	send_audio = 0;

	//+++ Del Task & Q
	OSTaskSuspend(PRIORITY_USB_CAM);
	OSTimeDly(1);
	OSTaskDelReq(PRIORITY_USB_CAM);
	OSTaskDel(PRIORITY_USB_CAM);
	OSQFlush(UVCTaskQ);
	OSQDel(UVCTaskQ, OS_DEL_ALWAYS, &err);
}
